Implement GtkBuildable on GtkIconFactory, to make it possible to register
authorJohan Dahlin <jdahlin@async.com.br>
Fri, 7 Mar 2008 00:08:16 +0000 (00:08 +0000)
committerJohan Dahlin <johan@src.gnome.org>
Fri, 7 Mar 2008 00:08:16 +0000 (00:08 +0000)
2008-03-06  Johan Dahlin  <jdahlin@async.com.br>

    * docs/reference/gtk/tmpl/gtkiconfactory.sgml:
    * gtk/gtkbuilder.c:
    * gtk/gtkbuilderprivate.h:
    * gtk/gtkiconfactory.c:
    * tests/buildertest.c:
    Implement GtkBuildable on GtkIconFactory, to make
    it possible to register custom stock icons.
    Fixes #517066

svn path=/trunk/; revision=19726

ChangeLog
docs/reference/gtk/tmpl/gtkiconfactory.sgml
gtk/gtkbuilder.c
gtk/gtkbuilderprivate.h
gtk/gtkiconfactory.c
tests/buildertest.c

index 8df4a6f1669de6d3ce9911f4a76b1f22e0563af2..84b2925b5e119b3275a974e7efa2ff341f4a2996 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2008-03-06  Johan Dahlin  <jdahlin@async.com.br>
+
+       * docs/reference/gtk/tmpl/gtkiconfactory.sgml:
+       * gtk/gtkbuilder.c:
+       * gtk/gtkbuilderprivate.h:
+       * gtk/gtkiconfactory.c:
+       * tests/buildertest.c:
+       Implement GtkBuildable on GtkIconFactory, to make
+       it possible to register custom stock icons.
+       Fixes #517066
+
 2008-03-06  Johan Dahlin  <johan@gnome.org>
 
        Make gtk-doc happy:
index 88a7229a3241e3f88cee0167f346bb628c77ee2c..db160c208d4f915cd729e08e4bd16da1caa456a8 100644 (file)
@@ -35,6 +35,77 @@ gtk_widget_render_icon(). These functions take the theme into account when
 looking up the icon to use for a given stock ID.
 </para>
 
+<refsect2 id="GtkIconFactory-BUILDER-UI"><title>GtkIconFactory as GtkBuildable</title>
+<para>
+GtkIconFactory supports a custom &lt;sources&gt; element, which 
+can contain multiple &lt;source&gt; elements. 
+The following attributes are allowed:
+<variablelist>
+
+<varlistentry>
+<term>stock-id</term>
+<listitem><para>The stock id of the source, a string.
+This attribute is mandatory</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term>filename</term>
+<listitem><para>The filename of the source, a string.
+This attribute is mandatory</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>icon-name</term>
+<listitem><para>The icon name for the source, a string.
+This attribute is optional.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>size</term>
+<listitem><para>Size of the icon, a #GtkIconSize enum value. 
+This attribute is optional.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>direction</term>
+<listitem><para>Direction of the source, a #GtkTextDirection enum value.
+This attribute is optional.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>state</term>
+<listitem><para>State of the source, a #GtkStateType enum value.
+This attribute is optional.</para>
+</listitem>
+</varlistentry>
+
+</variablelist>
+
+</para>
+<example>
+<title>A <structname>GtkIconFactory</structname> UI definition fragment.</title>
+<programlisting><![CDATA[
+<object class="GtkIconFactory" id="iconfactory1">
+  <sources>
+    <source stock-id="apple-red" filename="apple-red.png"/>
+  </sources>
+</object>
+<object class="GtkWindow" id="window1">
+  <child>
+    <object class="GtkButton" id="apple_button">
+      <property name="label">apple-red</property>
+      <property name="use-stock">True</property>
+    </object>
+  </child>
+</object>
+]]></programlisting>
+</example>
+</refsect2>
+
 <!-- ##### SECTION See_Also ##### -->
 <para>
 
index cc961352204f4daaa72d251b7312ab0ea0788073..11641035dcc53bc50bde9876f5ca07a0ad92e60b 100644 (file)
@@ -1234,18 +1234,7 @@ gtk_builder_value_from_string_type (GtkBuilder   *builder,
               return FALSE;
             }
 
-          if (g_path_is_absolute (string))
-            filename = g_strdup (string);
-          else
-            {
-              gchar *dirname;
-
-              dirname = g_path_get_dirname (builder->priv->filename);
-              filename = g_build_filename (dirname, string, NULL);
-
-              g_free (dirname);
-            }
-
+         filename = _gtk_builder_get_absolute_filename (builder, string);
           pixbuf = gdk_pixbuf_new_from_file (filename, &tmp_error);
 
           if (pixbuf == NULL)
@@ -1466,6 +1455,26 @@ gtk_builder_error_quark (void)
   return g_quark_from_static_string ("gtk-builder-error-quark");
 }
 
+gchar *
+_gtk_builder_get_absolute_filename (GtkBuilder *builder, const gchar *string)
+{
+  gchar *filename;
+  gchar *dirname = NULL;
+  
+  if (g_path_is_absolute (string))
+    return g_strdup (string);
+
+  if (builder->priv->filename &&
+      strcmp (builder->priv->filename, ".") != 0)
+    dirname = g_path_get_dirname (builder->priv->filename);
+  else
+    dirname = g_get_current_dir ();
+    
+  filename = g_build_filename (dirname, string, NULL);
+  g_free (dirname);
+  
+  return filename;
+}
 
 #define __GTK_BUILDER_C__
 #include "gtkaliasdef.c"
index 1a45aca2369d2f754c3efb223ea44cea3f1adb60..96f8fd456869b130e064805d33f9a033aea313ea 100644 (file)
@@ -125,5 +125,7 @@ gboolean  _gtk_builder_flags_from_string (GType       type,
 gchar * _gtk_builder_parser_translate (const gchar *domain,
                                       const gchar *context,
                                       const gchar *text);
+gchar *   _gtk_builder_get_absolute_filename (GtkBuilder *builder,
+                                             const gchar *string);
 
 #endif /* __GTK_BUILDER_PRIVATE_H__ */
index d8e8bff2794b4c059b25924274fa0671d2b1ed5f..80eb24def2375642cd1c185593a3df420cf2574f 100644 (file)
@@ -1,6 +1,6 @@
 /* GTK - The GIMP Toolkit
  * Copyright (C) 2000 Red Hat, Inc.
- *
+ *               2008 Johan Dahlin
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
@@ -37,6 +37,8 @@
 #include "gtkstock.h"
 #include "gtkwidget.h"
 #include "gtkintl.h"
+#include "gtkbuildable.h"
+#include "gtkbuilderprivate.h"
 #include "gtkalias.h"
 
 
@@ -83,6 +85,20 @@ struct _GtkIconSource
 };
 
 
+static void
+gtk_icon_factory_buildable_init  (GtkBuildableIface      *iface);
+
+static gboolean gtk_icon_factory_buildable_custom_tag_start (GtkBuildable     *buildable,
+                                                            GtkBuilder       *builder,
+                                                            GObject          *child,
+                                                            const gchar      *tagname,
+                                                            GMarkupParser    *parser,
+                                                            gpointer         *data);
+static void gtk_icon_factory_buildable_custom_tag_end (GtkBuildable *buildable,
+                                                      GtkBuilder   *builder,
+                                                      GObject      *child,
+                                                      const gchar  *tagname,
+                                                      gpointer     *user_data);
 static void gtk_icon_factory_finalize   (GObject             *object);
 static void get_default_icons           (GtkIconFactory      *icon_factory);
 static void icon_source_clear           (GtkIconSource       *source);
@@ -96,7 +112,9 @@ static GtkIconSize icon_size_register_intern (const gchar *name,
    0, 0, 0,                                                            \
    any_direction, any_state, any_size }
 
-G_DEFINE_TYPE (GtkIconFactory, gtk_icon_factory, G_TYPE_OBJECT)
+G_DEFINE_TYPE_WITH_CODE (GtkIconFactory, gtk_icon_factory, G_TYPE_OBJECT,
+                        G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
+                                               gtk_icon_factory_buildable_init))
 
 static void
 gtk_icon_factory_init (GtkIconFactory *factory)
@@ -113,6 +131,13 @@ gtk_icon_factory_class_init (GtkIconFactoryClass *klass)
   object_class->finalize = gtk_icon_factory_finalize;
 }
 
+static void
+gtk_icon_factory_buildable_init (GtkBuildableIface *iface)
+{
+  iface->custom_tag_start = gtk_icon_factory_buildable_custom_tag_start;
+  iface->custom_tag_end = gtk_icon_factory_buildable_custom_tag_end;
+}
+
 static void
 free_icon_set (gpointer key, gpointer value, gpointer data)
 {
@@ -2700,6 +2725,204 @@ _gtk_icon_factory_list_ids (void)
   return ids;
 }
 
+typedef struct {
+  GSList *sources;
+  gboolean in_source;
+  
+} IconFactoryParserData;
+
+typedef struct {
+  gchar            *stock_id;
+  gchar            *filename;
+  gchar            *icon_name;
+  GtkTextDirection  direction;
+  GtkIconSize       size;
+  GtkStateType      state;
+} IconSourceParserData;
+
+static void
+icon_source_start_element (GMarkupParseContext *context,
+                          const gchar         *element_name,
+                          const gchar        **names,
+                          const gchar        **values,
+                          gpointer             user_data,
+                          GError             **error)
+{
+  gint i;
+  gchar *stock_id = NULL;
+  gchar *filename = NULL;
+  gchar *icon_name = NULL;
+  GtkIconSize size = -1;
+  GtkTextDirection direction = -1;
+  GtkStateType state = -1;
+  IconFactoryParserData *parser_data;
+  IconSourceParserData *source_data;
+
+  parser_data = (IconFactoryParserData*)user_data;
+
+  if (!parser_data->in_source)
+    {
+      if (strcmp (element_name, "sources") != 0)
+       {
+         g_warning ("Unexpected element %s, expected <sources>", element_name);
+         return;
+       }
+      parser_data->in_source = TRUE;
+      return;
+    }
+  else
+    {
+      if (strcmp (element_name, "source") != 0)
+       {
+         g_warning ("Unexpected element %s, expected <source>", element_name);
+         return;
+       }
+    }
+  
+  for (i = 0; names[i]; i++)
+    {
+      if (strcmp (names[i], "stock-id") == 0)
+       stock_id = g_strdup (values[i]);
+      else if (strcmp (names[i], "filename") == 0)
+       filename = g_strdup (values[i]);
+      else if (strcmp (names[i], "icon-name") == 0)
+       icon_name = g_strdup (values[i]);
+      else if (strcmp (names[i], "size") == 0)
+       {
+         if (!_gtk_builder_flags_from_string (GTK_TYPE_ICON_SIZE,
+                                              values[i],
+                                              &size,
+                                              error))
+             return;
+       }
+      else if (strcmp (names[i], "direction") == 0)
+       {
+         if (!_gtk_builder_flags_from_string (GTK_TYPE_TEXT_DIRECTION,
+                                              values[i],
+                                              &direction,
+                                              error))
+             return;
+       }
+      else if (strcmp (names[i], "state") == 0)
+       {
+         if (!_gtk_builder_flags_from_string (GTK_TYPE_STATE_TYPE,
+                                              values[i],
+                                              &state,
+                                              error))
+             return;
+       }
+    }
+
+  if (!stock_id || !filename)
+    {
+      g_warning ("<source> requires a stock_id and a filename");
+      return;
+    }
+
+  source_data = g_slice_new (IconSourceParserData);
+  source_data->stock_id = stock_id;
+  source_data->filename = filename;
+  source_data->icon_name = icon_name;
+  source_data->size = size;
+  source_data->direction = direction;
+  source_data->state = state;
+
+  parser_data->sources = g_slist_prepend (parser_data->sources, source_data);
+}
+
+static const GMarkupParser icon_source_parser =
+  {
+    icon_source_start_element,
+  };
+
+static gboolean
+gtk_icon_factory_buildable_custom_tag_start (GtkBuildable     *buildable,
+                                            GtkBuilder       *builder,
+                                            GObject          *child,
+                                            const gchar      *tagname,
+                                            GMarkupParser    *parser,
+                                            gpointer         *data)
+{
+  g_assert (buildable);
+
+  if (strcmp (tagname, "sources") == 0)
+    {
+      IconFactoryParserData *parser_data;
+
+      parser_data = g_slice_new0 (IconFactoryParserData);
+      *parser = icon_source_parser;
+      *data = parser_data;
+      return TRUE;
+    }
+  return FALSE;
+}
+
+static void
+gtk_icon_factory_buildable_custom_tag_end (GtkBuildable *buildable,
+                                          GtkBuilder   *builder,
+                                          GObject      *child,
+                                          const gchar  *tagname,
+                                          gpointer     *user_data)
+{
+  GtkIconFactory *icon_factory;
+  
+  icon_factory = GTK_ICON_FACTORY (buildable);
+
+  if (strcmp (tagname, "sources") == 0)
+    {
+      IconFactoryParserData *parser_data;
+      GtkIconSource *icon_source;
+      GtkIconSet *icon_set;
+      GSList *l;
+
+      parser_data = (IconFactoryParserData*)user_data;
+
+      for (l = parser_data->sources; l; l = l->next)
+       {
+         IconSourceParserData *source_data = l->data;
+
+         icon_set = gtk_icon_factory_lookup (icon_factory, source_data->stock_id);
+         if (!icon_set)
+           {
+             icon_set = gtk_icon_set_new ();
+             gtk_icon_factory_add (icon_factory, source_data->stock_id, icon_set);
+           }
+
+         icon_source = gtk_icon_source_new ();
+
+         if (source_data->filename)
+           {
+             gchar *filename;
+             filename = _gtk_builder_get_absolute_filename (builder, source_data->filename);
+             gtk_icon_source_set_filename (icon_source, filename);
+             g_free (filename);
+           }
+         if (source_data->icon_name)
+           gtk_icon_source_set_icon_name (icon_source, source_data->icon_name);
+         if (source_data->size != -1)
+           gtk_icon_source_set_size (icon_source, source_data->size);
+         if (source_data->direction != -1)
+           gtk_icon_source_set_direction (icon_source, source_data->direction);
+         if (source_data->state != -1)
+           gtk_icon_source_set_state (icon_source, source_data->state);
+
+         /* Inline source_add() to avoid creating a copy */
+         g_assert (source->type != GTK_ICON_SOURCE_EMPTY);
+         icon_set->sources = g_slist_insert_sorted (icon_set->sources,
+                                                    icon_source,
+                                                    icon_source_compare);
+         gtk_icon_set_unref (icon_set);
+
+         g_free (source_data->stock_id);
+         g_free (source_data->filename);
+         g_free (source_data->icon_name);
+         g_slice_free (IconSourceParserData, source_data);
+       }
+      g_slist_free (parser_data->sources);
+      g_slice_free (IconFactoryParserData, parser_data);
+    }
+}
+
 #ifdef G_OS_WIN32
 
 /* DLL ABI stability backward compatibility versions */
index 60467d5448b0e68c7596fac69f107840df78dce0..65b90a99e7e75f332d96d05ee86b6eb877ce4276 100644 (file)
@@ -1793,6 +1793,34 @@ test_reference_counting (void)
   g_object_unref (builder);
 }
 
+static void
+test_icon_factory (void)
+{
+  GtkBuilder *builder;
+  const gchar buffer1[] =
+    "<interface>"
+    "  <object class=\"GtkIconFactory\" id=\"iconfactory1\">"
+    "    <sources>"
+    "      <source stock-id=\"apple-red\" filename=\"apple-red.png\"/>"
+    "    </sources>"
+    "  </object>"
+    "</interface>";
+  GObject *factory;
+  GtkIconSet *icon_set;
+  GtkWidget *image;
+  
+  builder = builder_new_from_string (buffer1, -1, NULL);
+  factory = gtk_builder_get_object (builder, "iconfactory1");
+  g_assert (factory != NULL);
+
+  icon_set = gtk_icon_factory_lookup (GTK_ICON_FACTORY (factory), "apple-red");
+  g_assert (icon_set != NULL);
+
+  gtk_icon_factory_add_default (GTK_ICON_FACTORY (factory));
+  image = gtk_image_new_from_stock ("apple-red", GTK_ICON_SIZE_BUTTON);
+  g_assert (image != NULL);
+}
+
 static void 
 test_file (const gchar *filename)
 {
@@ -1874,5 +1902,7 @@ main (int argc, char **argv)
   g_test_add_func ("/Builder/Value From String", test_value_from_string);
   g_test_add_func ("/Builder/Reference Counting", test_reference_counting);
   g_test_add_func ("/Builder/Window", test_window);
+  g_test_add_func ("/Builder/IconFactory", test_icon_factory);
+
   return g_test_run();
 }